package org.telegram.bot.service.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.StatusEnum;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SimpleTrie;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.telegram.bot.domain.BotGroup;
import org.telegram.bot.domain.BotMessageTextCheck;
import org.telegram.bot.domain.BotProhibitedWords;
import org.telegram.bot.emums.BotCommonEnums;
import org.telegram.bot.emums.RedisCacheKeyEnums;
import org.telegram.bot.handle.IBotSendMessage;
import org.telegram.bot.mapper.BotGroupMapper;
import org.telegram.bot.mapper.BotProhibitedWordsMapper;
import org.telegram.bot.service.IBotChatMemberService;
import org.telegram.bot.service.IBotProhibitedWordsService;
import org.telegram.bot.utils.BotCMDUtil;
import org.telegram.telegrambots.meta.api.methods.groupadministration.RestrictChatMember;
import org.telegram.telegrambots.meta.api.methods.updatingmessages.DeleteMessage;
import org.telegram.telegrambots.meta.api.objects.ChatPermissions;
import org.telegram.telegrambots.meta.api.objects.MessageEntity;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 违禁词管理Service业务层处理
 *
 * @author wyx
 * @date 2023-05-30
 */
@Slf4j
@Service
public class BotProhibitedWordsServiceImpl implements IBotProhibitedWordsService, IBotSendMessage
{
    @Resource
    private RedisCache redisCache;

    @Resource
    private BotProhibitedWordsMapper botProhibitedWordsMapper;
    
    @Resource
    private IBotChatMemberService botChatMemberService;
    
    @Resource
    private BotGroupMapper botGroupMapper;
    
    /**
     * 默认的敏感词的字典树，包含所有敏感词
     */
    @Getter
    private volatile LinkedHashMap<Long,SimpleTrie > defaultSensitiveWordTrie = new LinkedHashMap<>();
    
    /**
     * 初始化缓存
     */
    @PostConstruct
    public void init() {
        initLocalCache(null);
    }
    
    
    public void initLocalCache(List<Long> chatIds) {
        BotProhibitedWords query = new BotProhibitedWords();
        if(!CollectionUtils.isEmpty(chatIds)){
            query.setChatIds(chatIds);
        }
        // 第一步：查询数据
        List<BotProhibitedWords> list = botProhibitedWordsMapper.selectBotProhibitedWordsList(query);
        log.info("[initLocalCache][缓存敏感词，数量为:{}]", list.size());
        
        // 第二步：构建缓存
        initSensitiveWordTrie(list);
    }
    
    private void initSensitiveWordTrie(List<BotProhibitedWords> wordDOs) {
        //wordDOs根据getChatId分组
        Map<Long, List<BotProhibitedWords>> groupMap = wordDOs.stream().collect(Collectors.groupingBy(BotProhibitedWords::getChatId));
        if(!groupMap.isEmpty()) {
            //遍历groupMap
            for (Map.Entry<Long, List<BotProhibitedWords>> entry : groupMap.entrySet()) {
                if (entry.getKey() != null) {
                    // 构建敏感词字典树
                    SimpleTrie trie = new SimpleTrie(entry.getValue().stream().map(BotProhibitedWords::getWord).collect(Collectors.toList()));
                    
                    // 写入 defaultSensitiveWordTrie 缓存
                    this.defaultSensitiveWordTrie.put(entry.getKey(), trie);
                }
            }
        }
    }
    
    /**
     * 查询违禁词管理
     *
     * @param id 违禁词管理主键
     * @return 违禁词管理
     */
    @Override
    public BotProhibitedWords selectBotProhibitedWordsById(Long id)
    {
        return botProhibitedWordsMapper.selectById(id);
    }

    /**
     * 查询违禁词管理列表
     *
     * @param botProhibitedWords 违禁词管理
     * @return 违禁词管理
     */
    @Override
    public List<BotProhibitedWords> selectBotProhibitedWordsList(BotProhibitedWords botProhibitedWords)
    {
        Map<Long,BotGroup> botGroupsMap;
        List<Long> chats = botChatMemberService.getJoinChatList();
        if(!CollectionUtils.isEmpty(chats)){
            botProhibitedWords.setChatIds(chats);
            //查询群组
            botGroupsMap = botGroupMapper.selectList(new LambdaQueryWrapper<BotGroup>().in(BotGroup::getChatId,chats)).stream().collect(Collectors.toMap(BotGroup::getChatId,item->item));
        }else{
            return new ArrayList<>();
        }
        List<BotProhibitedWords> words = botProhibitedWordsMapper.selectBotProhibitedWordsList(botProhibitedWords);
        for (BotProhibitedWords word : words) {
            BotGroup botGroup = botGroupsMap.get(word.getChatId());
            if(botGroup!=null){
                word.getParams().put("chatName",botGroup.getTitle());
            }
        }
        return words;
    }

    /**
     * bot添加违禁词
     * @param cmd /添加违禁词 xx,111,bbb
     * @return
     */
    @Override
    public String insertBotProhibitedWords(Long chatId,String cmd)
    {
        String ret = "添加失败";
        cmd = cmd.replaceAll(BotCMDUtil.CMD_BLACKLIST,"").replace(" ","");
        String[] list = cmd.split(",");
        List<String> optList = new ArrayList<>();
        for (String w : list) {
            if(SqlHelper.retBool(botProhibitedWordsMapper.existsWord(w,chatId))){
                optList.add(w+"已存在");
            } else if(!SqlHelper.retBool(botProhibitedWordsMapper.insert(new BotProhibitedWords().setWord(w).setChatId(chatId)))){
                optList.add(w+"添加失败");
            }
            else{
                optList.add(w+"添加成功");
            }
        }
        ret = StringUtils.join(optList,"\n");
        initLocalCache(Collections.singletonList(chatId));
        return ret;
    }
    

    /**
     * 新增违禁词管理
     *
     * @param botProhibitedWords 违禁词管理
     * @return 结果
     */
    @Override
    public int insertBotProhibitedWords(BotProhibitedWords botProhibitedWords)
    {
        
        if(SqlHelper.retBool(botProhibitedWordsMapper.existsWord(botProhibitedWords.getWord(),botProhibitedWords.getChatId()))){
            throw new ServiceException("违禁词已存在");
        }
        botProhibitedWordsMapper.insert(botProhibitedWords);
        initLocalCache(Collections.singletonList(botProhibitedWords.getChatId()));
        return 1;
    }

    /**
     * 修改违禁词管理
     *
     * @param botProhibitedWords 违禁词管理
     * @return 结果
     */
    @Override
    public int updateBotProhibitedWords(BotProhibitedWords botProhibitedWords)
    {
        if(SqlHelper.retBool(botProhibitedWordsMapper.existsWord(botProhibitedWords.getWord(),botProhibitedWords.getChatId()))){
            throw new ServiceException("违禁词已存在");
        }
        botProhibitedWordsMapper.updateById(botProhibitedWords);
        initLocalCache(Collections.singletonList(botProhibitedWords.getChatId()));
        return 1;
    }

    /**
     * 批量删除违禁词管理
     *
     * @param ids 需要删除的违禁词管理主键
     * @return 结果
     */
    @Override
    public int deleteBotProhibitedWordsByIds(Long[] ids)
    {
        List<Long> idList = Arrays.asList(ids);
        List<BotProhibitedWords> list = botProhibitedWordsMapper.selectBatchIds(idList);
        List<Long> chatIds = list.stream().map(BotProhibitedWords::getChatId).collect(Collectors.toList());
        //chatIds 去重
        chatIds = new ArrayList<>(new HashSet<>(chatIds));
        int i = botProhibitedWordsMapper.deleteByIds(idList);
        initLocalCache(chatIds);
        return i;
    }

    /**
     * 删除违禁词管理信息
     *
     * @param id 违禁词管理主键
     * @return 结果
     */
    @Override
    public int deleteBotProhibitedWordsById(Long id)
    {
        BotProhibitedWords words = botProhibitedWordsMapper.selectById(id);
        int i = botProhibitedWordsMapper.deleteById(id);
        initLocalCache(Collections.singletonList(words.getChatId()));
        return i;
    }

    @Override
    public List<String> getAllProhibitedWords(){
        List<String> list = redisCache.getCacheList(RedisCacheKeyEnums.BOT_PROHIBITED_WORDS.getKey());
        if(CollectionUtils.isEmpty(list)){
            list = botProhibitedWordsMapper.selectAllWordsList();
            redisCache.setCacheList(RedisCacheKeyEnums.BOT_PROHIBITED_WORDS.getKey(),list);
        }
        return list;
    }
    
    /**
     * 检测文本是否包含违禁词
     * @param textCheck
     * @return true 有违禁词、false没有违禁词
     */
    @Override
    public Boolean checkText(BotMessageTextCheck textCheck) {
        // 判断是否有违禁词
        BotGroup groupInfo = getGroupInfo(textCheck.getChatId());
        if (groupInfo != null && groupInfo.getConfigWord() != null && StatusEnum.OK.getCode().equals(groupInfo.getConfigWord().getStatus())) {
            // 检测违规表情
            if (!CollectionUtils.isEmpty(textCheck.getEntities())) {
                // 先检测是否有违规表情
                Set<String> msgEmojiSet = textCheck.getEntities().stream()
                        .filter(entity -> BotCommonEnums.MESSAGE_ENTITY_CUSTOM_EMOJI.equals(entity.getType()))
                        .map(MessageEntity::getCustomEmojiId)
                        .collect(Collectors.toSet());
    
                // 快速判断是否有违规表情
                if (!CollectionUtils.isEmpty(msgEmojiSet) && !CollectionUtils.isEmpty(groupInfo.getBanEmojiSet()) && !Collections.disjoint(msgEmojiSet, groupInfo.getBanEmojiSet())) {
                    handleViolation(textCheck, groupInfo);
                    return true;
                }
                
                //检测是否有TG链接
                if(StatusEnum.OK.getCode().equals(groupInfo.getConfigWord().getBanTelegramUrl())){
                    Set<String> msgUrlSet = textCheck.getEntities().stream()
                            .filter(entity -> BotCommonEnums.MESSAGE_ENTITY_TEXT_LINK.equals(entity.getType()))
                            .map(MessageEntity::getUrl)
                            .collect(Collectors.toSet());
                    //快速检测msgUrlSet中的字符是否包含有BotCommonEnums.BOT_TELEGRAM_URL
                    if(!CollectionUtils.isEmpty(msgUrlSet) && msgUrlSet.stream().anyMatch(url -> url.contains(BotCommonEnums.BOT_TELEGRAM_URL))){
                        handleViolation(textCheck, groupInfo);
                        return true;
                    }
                    
                    //快速检测预览链接
                    if(StringUtils.isNotEmpty(textCheck.getLinkPreview()) && textCheck.getLinkPreview().contains(BotCommonEnums.BOT_TELEGRAM_URL) || (StringUtils.isNotEmpty(textCheck.getText()) && textCheck.getText().contains(BotCommonEnums.BOT_TELEGRAM_URL))){
                        handleViolation(textCheck, groupInfo);
                        return true;
                    }
                }
            }
    
            // 加入昵称检测
            if (StringUtils.isNotEmpty(textCheck.getNickName()) && groupInfo.getConfigWord().getBanNickName() != null && StatusEnum.OK.getCode().equals(groupInfo.getConfigWord().getBanNickName())) {
                textCheck.setText(textCheck.getText() + "," + textCheck.getNickName());
            }
            // true 文本合法 没有违禁词
            boolean has = true;
            if(StringUtils.isNotEmpty(textCheck.getText())){
                has = isTextValid(textCheck.getChatId(), textCheck.getText());
            }
            if (!has) {
                handleViolation(textCheck, groupInfo);
            }
            return !has;
        }
        return false;
    }
    
    /**
     * 违规处理
     * @param textCheck
     * @param groupInfo
     */
    private void handleViolation(BotMessageTextCheck textCheck, BotGroup groupInfo) {
    sendAsync(textCheck.getBotId(), DeleteMessage.builder().chatId(textCheck.getChatId()).messageId(textCheck.getMessageId()).build());
    // 是否封禁
    if (groupInfo.getConfigWord().getBanTime() >= 0) {
        Long timeL = DateUtil.currentSeconds() + groupInfo.getConfigWord().getBanTime();
        // 限制聊天
        sendAsync(textCheck.getBotId(), RestrictChatMember.builder()
                .chatId(textCheck.getChatId())
                .userId(textCheck.getUserId())
                .untilDate(timeL.intValue())
                .permissions(ChatPermissions.builder().canSendMessages(false).build())
                .build());
    }
}

    
    @Override
    public List<String> validateText(Long chatId,String text) {
        SimpleTrie simpleTrie = this.defaultSensitiveWordTrie.get(chatId);
        if(simpleTrie==null){
            return new ArrayList<>();
        }
        return simpleTrie.validate(text);
    }
    
    @Override
    public boolean isTextValid(Long chatId,String text) {
        SimpleTrie simpleTrie = this.defaultSensitiveWordTrie.get(chatId);
        if(simpleTrie==null){
            return true;
        }
        return simpleTrie.isValid(text);
    }
}
